﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Drawing;

namespace MonoPhysics
{
    public class Polygon : IShape
    {
        private CircularLinkedListNode<PointD, RigidBody> bounds;
        private RigidBody pere;
        private PointD center = new PointD(0, 0);
        private BroadBox AABB;
        private int color;
        private SphereTree st;
        private float aire;
        private PointD nonZeroCentroid;
        private MonoPhysics.BoundingBox boundingBox;

        public MonoPhysics.BoundingBox BoundingBox
        { get { return boundingBox; } }

        public PointD NonZeroCentroid
        {
            get { return this.nonZeroCentroid; }
            set { this.nonZeroCentroid = value; }
        }

        public float Aire
        {
            get { return this.aire; }
            set { this.aire = value; }
        }

        public int Color
        { get { return this.color; } }

        public RigidBody Pere
        {
            get { return this.pere; }
            set { this.pere = value; }
        }

        public SphereTree Tree
        {
            get { return this.st; }
            set { this.st = value; }
        }

        public BroadBox BoundaryRect
        { get { return AABB; } }

        public CircularLinkedListNode<PointD, RigidBody> Points
        {
            get { return this.bounds; }
            set { this.bounds = value; }
        }

        public PointD Centroid
        { get { return this.center; } }

        //public int NumberOfSides
        //{ get { return this.bounds.Count; } }

        public Polygon(List<PointD> pts, RigidBody pere)
        {
            if (pts != null || pts.Count < 3)
            {
                this.pere = pere;
                // Verification sens
                bool countclockWise;
                this.aire = MathGeometryHelper.CalcConvexSurface(pts, out countclockWise);
                if (!countclockWise)
                    pts.Reverse();
                List<PointD> pbounds = new List<PointD>();
                nonZeroCentroid = GeometryHelper.GetCentroid(pts, this.aire);
                foreach (PointD pf in pts)
                    pbounds.Add(new PointD(pf.X - nonZeroCentroid.X, pf.Y - nonZeroCentroid.Y));
                this.color = this.GetHashCode();
                this.bounds = CircularLinkedListNode<PointD, RigidBody>.BuildColoredCircularLinkedListNode(pbounds, pere, this.color);
                this.AABB = new BroadBox();
                this.AABB.min = new EndPoint[2];
                this.AABB.min[0] = new EndPoint();
                this.AABB.min[1] = new EndPoint();
                this.AABB.max = new EndPoint[2];
                this.AABB.max[0] = new EndPoint();
                this.AABB.max[1] = new EndPoint();
                this.AABB.own = pere;
                this.UpdateAABBAndSafePoint();
                this.st = new SphereTree(this, 10, this.pere);
                this.boundingBox = new MonoPhysics.BoundingBox();
                this.boundingBox.parent = this.pere;
                this.boundingBox.XMax = new LinkedListNode<MonoPhysics.EndPoint2>(new MonoPhysics.EndPoint2(this.boundingBox, this.AABB.max[0].value, false, true));
                this.boundingBox.XMin = new LinkedListNode<MonoPhysics.EndPoint2>(new MonoPhysics.EndPoint2(this.boundingBox, this.AABB.min[0].value, true, true));
                this.boundingBox.YMax = new LinkedListNode<MonoPhysics.EndPoint2>(new MonoPhysics.EndPoint2(this.boundingBox, this.AABB.max[1].value, false, false));
                this.boundingBox.YMin = new LinkedListNode<MonoPhysics.EndPoint2>(new MonoPhysics.EndPoint2(this.boundingBox, this.AABB.min[1].value, true, false));
            }
            else
                throw new Monophysics.PhysicException();
        }


        public void UpdatePolygoneAsFusion(List<PointD> pts)
        {
            if (pts != null || pts.Count < 3)
            {
                List<PointD> pbounds = new List<PointD>();
                bool countclockWise;
                this.aire = MathGeometryHelper.CalcConvexSurface(pts, out countclockWise);
                if (!countclockWise)
                    pts.Reverse();

                nonZeroCentroid = GeometryHelper.GetCentroid(pts, this.aire);
                foreach (PointD pf in pts)
                    pbounds.Add(new PointD(pf.X - nonZeroCentroid.X, pf.Y - nonZeroCentroid.Y));
                this.bounds = CircularLinkedListNode<PointD, RigidBody>.BuildColoredCircularLinkedListNode(pbounds, pere, this.color);
                this.UpdateAABBAndSafePoint();
                /*
                this.AABB.max[0].value += (float)(this.pere.Pos.X-nonZeroCentroid.X);
                this.AABB.min[0].value += (float)(this.pere.Pos.X - nonZeroCentroid.X);
                this.AABB.max[1].value += (float)(this.pere.Pos.Y - nonZeroCentroid.Y);
                this.AABB.min[1].value += (float)(this.pere.Pos.Y - nonZeroCentroid.Y);
                */
                this.st = new SphereTree(this, 10, this.pere);
            }
            else
                throw new Monophysics.PhysicException();
        }


        public void SetPointsHead(CircularLinkedListNode<PointD, RigidBody> newHead)
        { this.bounds = newHead; }

        public void UpdateAABBAndSafePoint()
        { GeometryHelper.GetMinimalRectAndExtremum(this.bounds, ref this.bounds, ref this.AABB); }

        public void InvertLinks()
        {
            CircularLinkedListNode<PointD, RigidBody> parc = bounds;
            do
            {
                foreach (CircularLinkedListColoredNode<PointD, RigidBody> c in parc.nodes)
                    MathGeometryHelper.swap<CircularLinkedListNode<PointD, RigidBody>>(ref c.fils, ref c.parent);
                parc = parc.nodes[1].parent;
            }
            while (parc != bounds);
        }

        public void ForceUpdateBoudaryRectValuesWithoutCallBroadPhase()
        {
            this.boundingBox.XMax.Value.value = this.AABB.max[0].value + this.pere.Pos.X;
            this.boundingBox.XMin.Value.value = this.AABB.min[0].value + this.pere.Pos.X;
            this.boundingBox.YMax.Value.value = this.AABB.max[1].value + this.pere.Pos.Y;
            this.boundingBox.YMin.Value.value = this.AABB.min[1].value + this.pere.Pos.Y;
        }
    }
}